///////////////////////////////////////////////////////////////////////////////
// Name:        sheet.h
// Purpose:     wxSheet and related classes
// Author:      John Labenski, Michael Bedward (based on code by Julian Smart, Robin Dunn)
// Modified by:
// Created:     1/08/1999
// RCS-ID:      $Id: sheet.h,v 1.23 2007/07/07 15:52:47 jrl1 Exp $
// Copyright:   (c) John Labenski, Michael Bedward
// Licence:     wxWidgets licence
///////////////////////////////////////////////////////////////////////////////

#ifndef __WX_SHEET_H__
#define __WX_SHEET_H__

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
    #pragma interface "sheet.h"
#endif

#include "wx/sheet/sheetdef.h" // Always include sheetdef.h first
#include "wx/sheet/sheetatr.h" // might as well include everything
#include "wx/sheet/sheetsel.h"
#include "wx/sheet/sheetedt.h"
#include "wx/sheet/sheetren.h"
#include "wx/sheet/sheettbl.h"
#include "wx/sheet/sheetedg.h"
#include "wx/pen.h"
#include "wx/window.h"
#include "wx/scrolbar.h"
#include "wx/clntdata.h"

class WXDLLEXPORT wxTimer;
class WXDLLEXPORT wxTimerEvent;

// ----------------------------------------------------------------------------
// wxSheetChildWindow - Child window of the wxSheet, forwards events to sheet
// Only very sophisticated implementations will ever need to use or override
// this class.
// ----------------------------------------------------------------------------
class WXDLLIMPEXP_SHEET wxSheetChildWindow : public wxWindow
{
public:
    wxSheetChildWindow( wxSheet *parent, wxWindowID id = wxID_ANY,
                        const wxPoint &pos = wxDefaultPosition,
                        const wxSize &size = wxDefaultSize,
                        long style = wxWANTS_CHARS|wxBORDER_NONE|wxCLIP_CHILDREN,
                        const wxString& name = wxT("wxSheetChildWindow") );

    // implementation
    void OnPaint( wxPaintEvent& event );
    void OnMouse( wxMouseEvent& event );
    void OnKeyAndChar( wxKeyEvent& event );
    void OnFocus( wxFocusEvent& event );
    void OnEraseBackground( wxEraseEvent& );

    wxSheet* GetOwner() const { return m_owner; }

    wxSheet *m_owner;
    int      m_mouseCursor; // remember the last cursor set for this window

private:
    DECLARE_ABSTRACT_CLASS(wxSheetChildWindow)
    DECLARE_EVENT_TABLE()
    DECLARE_NO_COPY_CLASS(wxSheetChildWindow)
};

//-----------------------------------------------------------------------------
// wxSheetRefData - ref counted data to share with refed wxSheets
//
// Please use accessors in the wxSheet to adjust the values, for very
// complicated scenarios adjust these as a last resort.
//
// This is separated from the wxSheetTable since these are required for the
// sheet and the table may not have been assigned yet.
//
// This MUST always exist as the wxObject::m_refData of the wxSheet. It is
// automatically created in wxSheet::Init, if you wish to use your own
// subclassed refdata then in your wxSheet constructor (or Create) function call
// UnRef to delete the original and m_refData=new MySheetRefData to set the new.
//-----------------------------------------------------------------------------
class WXDLLIMPEXP_SHEET wxSheetRefData : public wxObjectRefData
{
public:
    wxSheetRefData();
    virtual ~wxSheetRefData();
    // Find/Add/Remove sheets that share this data - used for wxSheetSplitter
    int  FindSheet(wxSheet* sheet) const;
    bool HasSheet(wxSheet* sheet) const { return FindSheet(sheet) != wxNOT_FOUND; }
    void AddSheet(wxSheet* sheet);
    void RemoveSheet(wxSheet* sheet);
    wxSheet *GetSheet(size_t n) const { return (wxSheet*)m_sheets.Item(n); }
    size_t GetSheetCount() const { return m_sheets.GetCount(); }

    // implementation ---------------------------------------------------------

    wxArrayPtrVoid m_sheets;             // list of sheets sharing this data

    wxSheetTable *m_table;               // the table that stores the values
    bool          m_ownTable;            // if we own the table then delete it when done

    wxSheetTypeRegistry* m_typeRegistry; // editor/renderer registry

    wxSheetArrayEdge m_rowEdges;         // calculates edge positions and
    wxSheetArrayEdge m_colEdges;         // stores num rows/cols

    // width of row labels and height of col labels
    int      m_rowLabelWidth;
    int      m_colLabelHeight;

    // the size of the margin to the right and bottom of the cell area
    wxSize   m_marginSize;

    wxSize   m_scrollThumbSize;
    int      m_equal_col_widths;

    wxColour m_gridLineColour;
    int      m_gridLinesEnabled;
    wxPen    m_cursorCellHighlightPen;
    wxPen    m_cursorCellHighlightROPen;
    wxColour m_labelOutlineColour;

    wxCursor m_rowResizeCursor;
    wxCursor m_colResizeCursor;

    int      m_batchCount;  // are we delaying redrawing now
    int      m_editable;    // applies to whole grid - brute force readonly
    int      m_canDrag;     // enum wxSheetDragCellSize_Type

    wxSheetCoords m_cursorCoords;

    wxSheetBlock      m_selectingBlock;   // during selection !Empty
    wxSheetCoords     m_selectingAnchor;  // corner of active selection, other is usually cursor
    wxSheetSelection* m_selection;
    int               m_selectionMode;    // wxSheetSelectionMode_Type
    wxColour          m_selectionBackground;
    wxColour          m_selectionForeground;

    // the default cell attr objects for cells that don't have their own
    wxSheetCellAttr m_defaultGridCellAttr;
    wxSheetCellAttr m_defaultRowLabelAttr;
    wxSheetCellAttr m_defaultColLabelAttr;
    wxSheetCellAttr m_defaultCornerLabelAttr;

    wxSheetCellEditor m_cellEditor;       // valid only when editing
    wxSheetCoords     m_cellEditorCoords;

    wxSheetValueProviderSparseString m_copiedData;
    bool                             m_pasting;
};

// ----------------------------------------------------------------------------
// wxSheet
// ----------------------------------------------------------------------------
class WXDLLIMPEXP_SHEET wxSheet : public wxWindow
{
public:
    wxSheet() : wxWindow() { Init(); }

    wxSheet( wxWindow *parent, wxWindowID id,
             const wxPoint& pos = wxDefaultPosition,
             const wxSize& size = wxDefaultSize,
             long style = wxWANTS_CHARS,
             const wxString& name = wxT("wxSheet") ) : wxWindow()
    {
        Init();
        Create(parent, id, pos, size, style, name);
    }

    bool Create( wxWindow *parent, wxWindowID id,
                 const wxPoint& pos = wxDefaultPosition,
                 const wxSize& size = wxDefaultSize,
                 long style = wxWANTS_CHARS,
                 const wxString& name = wxT("wxSheet") );

    virtual ~wxSheet();
    virtual bool Destroy(); // override wxWindow::Destroy()

    // ref another wxSheet's ref data - see usage in wxSheetSplitter
    void RefSheet(wxSheet* otherSheet);
    // Create a new wxSheet with same parent, used for wxSheetSplitter.
    //   override this so that the top left sheet in the splitter can return
    //   a "new MySheet" for the other sheets as necessary
    //   This is one of the few ways for the splitter to create new sheets.
    virtual wxSheet* Clone(wxWindow *parent, wxWindowID id = wxID_ANY,
						   const wxPoint& pos = wxDefaultPosition,
                           const wxSize& size = wxDefaultSize,
                           long style = wxWANTS_CHARS,
                           const wxString& name = wxT("wxSheet"));

    // override wxWindow::Enable to ensure proper refresh
    virtual bool Enable(bool enable = true);

    // ------------------------------------------------------------------------
    // Create/Set/Get wxSheetTable - This is the underlying data to be
    // displayed

    wxSheetTable* GetTable() const { return GetSheetRefData()->m_table; }
    // Set your own table derived from wxSheetTable, if takeOwnership then
    //   the table will be deleted when this window is destroyed or a new
    //   table set.
    bool SetTable( wxSheetTable *table, bool takeOwnership );

    // Create a generic wxSheetTable using string data containers.
    //   See this function to see how to setup the table and how to use
    //      SetTable() for the case where you want to customize things.
    bool CreateGrid( int numRows, int numCols, int options = 0 );

    // ------------------------------------------------------------------------
    // Dimensions of the number of cells on the sheet and helper cell functions

    int GetNumberRows() const { return GetSheetRefData()->m_rowEdges.GetCount(); }
    int GetNumberCols() const { return GetSheetRefData()->m_colEdges.GetCount(); }

    // Is the coords anywhere in labels or grid, -1 to GetNumberRows/Cols()-1
    bool ContainsCell( const wxSheetCoords& coords ) const
        { return (coords.m_row >= -1) && (coords.m_col >= -1) &&
                 (coords.m_row < GetNumberRows()) &&
                 (coords.m_col < GetNumberCols()); }

    // returns true if the coords are within the grid cells of the sheet
    bool ContainsGridRow( int row ) const { return (row >= 0) && (row < GetNumberRows()); }
    bool ContainsGridCol( int col ) const { return (col >= 0) && (col < GetNumberCols()); }
    bool ContainsGridCell( const wxSheetCoords& coords ) const
        { return ContainsGridRow(coords.m_row) && ContainsGridCol(coords.m_col); }

    // returns true if the coords are within the row/col label cells
    bool ContainsRowLabelCell( const wxSheetCoords& coords ) const
        { return (coords.m_col == -1) && ContainsGridRow(coords.m_row); }
    bool ContainsColLabelCell( const wxSheetCoords& coords ) const
        { return (coords.m_row == -1) && ContainsGridCol(coords.m_col); }

    // static helper functions to determine what type of cell it is.
    // These functions do not check the cell's validity.

    static bool IsGridCell(const wxSheetCoords& coords)
        { return (coords.m_row >=  0) && (coords.m_col >=  0); }
    static bool IsLabelCell(const wxSheetCoords& coords)
        { return (coords.m_row >= -1) && (coords.m_col >= -1) &&
                ((coords.m_row == -1) || (coords.m_col == -1)); }
    static bool IsRowLabelCell(const wxSheetCoords& coords)
        { return (coords.m_row >=  0) && (coords.m_col == -1); }
    static bool IsColLabelCell(const wxSheetCoords& coords)
        { return (coords.m_row == -1) && (coords.m_col >=  0); }
    static bool IsCornerLabelCell(const wxSheetCoords& coords)
        { return (coords.m_row == -1) && (coords.m_col == -1); }

    // Get an enum value of what window the coords are meant for
    static wxSheetCell_Type GetCellCoordsType(const wxSheetCoords& coords);

    // "clear" the contents of the grid (depends on table's Clear() function)
    //   the string implementations clear the cell values, not the # rows/cols
    void ClearValues(int update = wxSHEET_UpdateValues);

    // Insert/Add/DeleteRows/Cols to the grid cells.
    //   'update' contains or'ed values of enum wxSheetUpdate_Type.
    //   You proabably want UpdateAll unless you ensure that no problems will
    //   occur or you will update some other way.
    bool InsertRows( size_t row, size_t numRows, int update = wxSHEET_UpdateAll )
        { return UpdateRows(row, numRows, update); }
    bool AppendRows( size_t numRows, int update = wxSHEET_UpdateAll )
        { return UpdateRows(GetNumberRows(), numRows, update); }
    bool DeleteRows( size_t row, size_t numRows, int update = wxSHEET_UpdateAll )
        { return UpdateRows(row, -int(numRows), update); }

    bool InsertCols( size_t col, size_t numCols, int update = wxSHEET_UpdateAll )
        { return UpdateCols(col, numCols, update); }
    bool AppendCols( size_t numCols, int update = wxSHEET_UpdateAll )
        { return UpdateCols(GetNumberCols(), numCols, update); }
    bool DeleteCols( size_t col, size_t numCols, int update = wxSHEET_UpdateAll )
        { return UpdateCols(col, -int(numCols), update); }

    // Set exactly the number of rows or cols, these functions Append or
    // Delete rows/cols to/from the end. If you are setting attributes for
    // particular cells/rows/cols you probably won't want to use these.
    bool SetNumberRows( size_t rows, int update = wxSHEET_UpdateAll );
    bool SetNumberCols( size_t cols, int update = wxSHEET_UpdateAll );
    bool SetNumberCells( size_t rows, size_t cols, int update = wxSHEET_UpdateAll )
        { bool r1 = SetNumberRows(rows, update), r2 = SetNumberCols(cols, update); return r1 || r2; }

    // Inserting/Appending/Deleting rows/cols functions are forwarded here
    //   and then sent to the wxSheetTable::UpdateRows/Cols functions.
    //   If numRows/Cols > 0 then InsertRows/Cols at row/col else if < 0 Delete.
    //   If row/col == GetNumberRows/Cols && numRows/Cols > 0 then append to end.
    virtual bool UpdateRows( size_t row, int numRows, int update = wxSHEET_UpdateAll );
    virtual bool UpdateCols( size_t col, int numCols, int update = wxSHEET_UpdateAll );

    // ------------------------------------------------------------------------
    // Dimensions of the row and column sizes

    // Get/Set the default height/width of newly created rows/cols.
    //   If 'resizeExisting' then resize all to match the newly set default
    int  GetDefaultRowHeight() const { return GetSheetRefData()->m_rowEdges.GetDefaultSize(); }
    int  GetDefaultColWidth()  const { return GetSheetRefData()->m_colEdges.GetDefaultSize(); }
    void SetDefaultRowHeight( int height, bool resizeExistingRows = false );
    void SetDefaultColWidth( int width, bool resizeExistingCols = false );

    // Get/Set the absolute min row/col width/height, use 0 for no min size.
    //   Call during grid creation, since existing rows/cols are not resized.
    //   This value is used when dragging cell size with the mouse if no
    //   particular min size for a row/col has been set.
    int  GetMinimalAcceptableRowHeight() const { return GetSheetRefData()->m_rowEdges.GetMinAllowedSize(); }
    int  GetMinimalAcceptableColWidth()  const { return GetSheetRefData()->m_colEdges.GetMinAllowedSize(); }
    void SetMinimalAcceptableRowHeight(int height) { GetSheetRefData()->m_rowEdges.SetMinAllowedSize(height); }
    void SetMinimalAcceptableColWidth(int width)   { GetSheetRefData()->m_colEdges.SetMinAllowedSize(width); }

    // Don't allow specific rows/cols to be resized smaller than this.
    //   Call during grid creation, existing rows/cols are not resized
    //   The setting is cleared to default val if width/height is < min acceptable.
    int  GetMinimalRowHeight(int row) const { return GetSheetRefData()->m_rowEdges.GetMinSize(row); }
    int  GetMinimalColWidth(int col) const  { return GetSheetRefData()->m_colEdges.GetMinSize(col); }
    void SetMinimalRowHeight(int row, int height) { GetSheetRefData()->m_rowEdges.SetMinSize(row, height); }
    void SetMinimalColWidth(int col, int width)   { GetSheetRefData()->m_colEdges.SetMinSize(col, width); }

    // Set the height of a row or width of a col, uses -1 coords for labels.
    //  Use height/width = -1 to autosize from the row/col labels.
    //  If 'ignore_min_size' then set the value as is.
    void SetRowHeight( int row, int height, bool ignore_min_size = false );
    void SetColWidth(  int col, int width, bool ignore_min_size = false );

    // Get the height/top/bottom for rows, uses -1 coords for labels.
    int GetRowHeight(int row) const;
    int GetRowTop(int row) const;
    int GetRowBottom(int row) const;
    // Get the width/left/right for cols, uses -1 coords for labels.
    int GetColWidth(int col) const;
    int GetColLeft(int col) const;
    int GetColRight(int col) const;
    // Get the width, height of a single cell as a wxSize, uses -1 coords for labels.
    //  This does not account for spanned cells.
    wxSize GetCellSize(const wxSheetCoords& coords) const;

    // Does the cell have a non-zero width and height, may not be visible, uses -1 coords for labels
    bool IsCellShown( const wxSheetCoords& coords ) const;

    // The grid may occupy more space than needed for its rows/columns, this
    // function allows you to set how big this margin to the right and bottom is.
    void SetMargins(int width, int height)
        { GetSheetRefData()->m_marginSize.x = wxMax(0, width);
          GetSheetRefData()->m_marginSize.y = wxMax(0, height); }

    // Get the renderer's best size for the cell, uses -1 coords for labels
    wxSize GetCellBestSize(const wxSheetCoords& coords, wxDC *dc = NULL) const;
    // Get the best height of a row or the best width of a col using the
    //   renderer's best size, iterating though all cells in the row or col.
    int GetRowBestHeight(int row) const;
    int GetColBestWidth(int col) const;

    // ------------------------------------------------------------------------
    // Row/Col label size

    // Get/Set the row/col label widths,
    //   if zero_not_shown and row/col & corner not shown return 0
    int  GetRowLabelWidth(bool zero_not_shown = true)  const;
    int  GetColLabelHeight(bool zero_not_shown = true) const;
    void SetRowLabelWidth( int width );
    void SetColLabelHeight( int height );

    // ------------------------------------------------------------------------
    // Auto sizing of the row/col widths/heights

    // Automatically size the col/row to fit to its contents. If 'setAsMin',
    // then the optimal width will also be set as minimal width for this column.
    // Returns the width or height used.
    int AutoSizeRow( int row, bool setAsMin = true );
    int AutoSizeCol( int col, bool setAsMin = true );

    // Auto size all columns (very ineffective for big grids!)
    void AutoSizeRows( bool setAsMin = true );
    void AutoSizeCols( bool setAsMin = true );

    // Auto size the grid, by making the columns/rows the "right" size
    // and also set the grid size to just fit its contents.
    void AutoSize( bool setAsMin = true );

    // Autosize row height depending on label text.
    void AutoSizeRowLabelHeight( int row );
    // Autosize column width depending on label text.
    void AutoSizeColLabelWidth( int col );

    // Force the col widths to be of equal size so that they fit within the
    // the window size. This is maintained even when the window is resized.
    // The col widths will not be sized less than min_width in pixels.
    // Use this if you know that the window will be of a reasonable size to
    // fit the cols, but you don't want to track the EVT_SIZE yourself.
    // use a min_width = 0 to turn it off
    void SetEqualColWidths(int min_width);

    // ------------------------------------------------------------------------
    // Row/Col drag resizing enabled or disabled
    //
    // if CanDragRow/ColSize is true, the rows/cols can be resized by the mouse.
    // if CanDragGridSize is true, you can resize the rows/cols in the grid window
    //   else you resize in the label windows (if CanDragRow/ColSize is true).

    void EnableDragRowSize( bool enable = true ) { SetDragCellSize(wxSHEET_DragLabelRowHeight, enable); }
    void EnableDragColSize( bool enable = true ) { SetDragCellSize(wxSHEET_DragLabelColWidth, enable); }
    void EnableDragGridSize(bool enable = true ) { SetDragCellSize(wxSHEET_DragGridCellSize, enable); }
    void DisableDragRowSize()                    { EnableDragRowSize( false ); }
    void DisableDragColSize()                    { EnableDragColSize( false ); }
    void DisableDragGridSize()                   { EnableDragGridSize(false); }
    bool CanDragRowSize()  const                 { return (GetDragCellSize() & wxSHEET_DragLabelRowHeight) != 0; }
    bool CanDragColSize()  const                 { return (GetDragCellSize() & wxSHEET_DragLabelColWidth) != 0; }
    bool CanDragGridSize() const                 { return (GetDragCellSize() & wxSHEET_DragGridCellSize) != 0; }

    // Directly set the dragging of the cell size use wxSheetDragCellSize_Type enums
    void SetDragCellSize( int type ) { GetSheetRefData()->m_canDrag = type; }
    void SetDragCellSize( int type, bool enable )
        { SetDragCellSize( enable ? (GetDragCellSize() | type) : (GetDragCellSize() & (~type)) ); }
    int GetDragCellSize() const { return GetSheetRefData()->m_canDrag; }

    // ------------------------------------------------------------------------
    // Grid line, cell highlight, selection colouring

    // Draw the grid lines, wxHORIZONAL | wxVERTICAL (wxBOTH), 0 for none
    void EnableGridLines( int dir = wxBOTH );
    int  GridLinesEnabled() const { return GetSheetRefData()->m_gridLinesEnabled; }

    const wxColour& GetGridLineColour() const { return GetSheetRefData()->m_gridLineColour; }
    void            SetGridLineColour( const wxColour& colour );

    // Get/Set the pen to highlight the cursor cell, wxNullPen to not draw highlight
    const wxPen& GetCursorCellHighlightPen() const   { return GetSheetRefData()->m_cursorCellHighlightPen; }
    const wxPen& GetCursorCellHighlightROPen() const { return GetSheetRefData()->m_cursorCellHighlightROPen; }
    void         SetCursorCellHighlightPen(const wxPen& pen);
    void         SetCursorCellHighlightROPen(const wxPen& pen);

    // Get/Set the colour bounding the labels to give 3-D effect
    const wxColour& GetLabelOutlineColour() const { return GetSheetRefData()->m_labelOutlineColour; }
    void            SetLabelOutlineColour( const wxColour& colour );

    const wxColour& GetSelectionBackground() const { return GetSheetRefData()->m_selectionBackground; }
    const wxColour& GetSelectionForeground() const { return GetSheetRefData()->m_selectionForeground; }
    void            SetSelectionBackground(const wxColour& c) { GetSheetRefData()->m_selectionBackground = c; }
    void            SetSelectionForeground(const wxColour& c) { GetSheetRefData()->m_selectionForeground = c; }

    // ------------------------------------------------------------------------
    // Span, cells can span across multiple cells, hiding cells below
    //
    // Normally cells are of size 1x1, but they can be larger.
    // The other cells can still have values and attributes, but they
    // will not be used since GetCellOwner is used for most coord operations
    // so that the underlying cell values and attributes are ignored.
    // The span for the owner cell is 1x1 or larger, the span for other
    // cells contained within the spanned block have a cell span of <= 0, such
    // that coords + GetCellSpan() = the owner cell
    //
    // *** To completely override spanning handling you may provide alternate
    // *** HasSpannedCells, GetCellBlock, SetCellSpan

    // Are there any spanned cells at all?
    virtual bool HasSpannedCells() const;

    // If cell is part of a spanning cell, return owner's coords else input coords.
    wxSheetCoords GetCellOwner( const wxSheetCoords& coords ) const;
    // Get a block of the cell, unless a spanned cell it's of size 1,1
    //   note: the top left of block is the owner cell coords
    virtual wxSheetBlock GetCellBlock( const wxSheetCoords& coords ) const;
    // Get the span of a cell, the owner (top right) cell always has a span of
    //  (1, 1) or greater. The other cells in a spanned block will have a span
    //  (<1, <1) such that, coords + coordsSpan = ownerCoords
    wxSheetCoords GetCellSpan( const wxSheetCoords& coords ) const;
    // Set the span of a cell, must be 1x1 or greater,
    // To remove a spanned cell set it to a cell of size 1x1
    // For grid cells the whole block must be contained within the grid cells
    //  and if the block intersects a previously spanned cell block the top left
    //  corners must match up.
    // Row and Col labels can span cells as well, spanned row labels must have a
    //  width of 1 and a height of >= 1, col labels a height of 1 and width >= 1
    virtual void SetCellSpan( const wxSheetBlock& block );
    void SetCellSpan( const wxSheetCoords& coords, const wxSheetCoords& numRowsCols )
        { SetCellSpan(wxSheetBlock(coords, numRowsCols.m_row, numRowsCols.m_col)); }

    // Get a pointer to the spanned blocks to iterate through, may return NULL.
    const wxSheetSelection* GetSpannedBlocks() const { return GetTable() ? GetTable()->GetSpannedBlocks() : NULL; }

    // ------------------------------------------------------------------------
    // Get/Set attributes for the cells, row, col, corner labels

    // See wxSheetAttr_Type for a description of the type of attribute

    // The coords are specified as
    //   Grid area    : (0 <= row < GetNumberRows), (0 <= col < GetNumberCols)
    //   Corner label : row = col = -1
    //   Row labels   : (0 <= row < GetNumberRows), col = -1
    //   Col labels   : row = -1, (0 <= col < GetNumberCols)

    // For the wxSHEET_AttrDefault type the coords should be contained within the
    //   size of the sheet, but the particular values are not used.
    //   see const wxGridCellSheetCoords = (0,0) for example, also
    //   wxRowLabelSheetCoords, wxColLabelSheetCoords, wxCornerLabelSheetCoords

    // *** To completely override attr handling you may provide alternate
    // *** GetOrCreateAttr, GetAttr, and SetAttr functions.

    // Make sure that the last default attr of initAttr is defAttr
    //   This is called internally when you call SetAttr and should not be
    //   needed unless you want to specially chain together attributes.
    bool InitAttr( wxSheetCellAttr& initAttr, const wxSheetCellAttr& defAttr ) const;

    // Get an attribute for the coords if it exists or create a new one
    // and put it into the table which puts it in the attr provider.
    // The type may be only be wxSHEET_AttrDefault/Cell/Row/Col for the grid cells
    // and wxSHEET_AttrDefault/Cell for the labels
    virtual wxSheetCellAttr GetOrCreateAttr( const wxSheetCoords& coords,
                                             wxSheetAttr_Type type ) const;

    // Get the attribute for any area depending on the coords and type
    // returns a valid attr if the coords are valid and type = wxSHEET_AttrAny
    // The type may be only be wxSHEET_AttrDefault/Cell/Row/Col/Any for the grid cells
    // and wxSHEET_AttrDefault/Cell/Any for the labels
    virtual wxSheetCellAttr GetAttr( const wxSheetCoords& coords,
                                     wxSheetAttr_Type type = wxSHEET_AttrAny) const;
    // Set the attribute for any area depending on the coords
    // The type may only be wxSHEET_AttrDefault/Cell/Row/Col for the grid cells
    // and wxSHEET_AttrDefault/Cell for the labels.
    // You may remove previously set attributes by setting wxNullSheetCellAttr.
    virtual void SetAttr(const wxSheetCoords& coords, const wxSheetCellAttr& attr,
                         wxSheetAttr_Type type );

    // ------ Simplified functions for accessing the attributes ---------------
    // Get an attribute for the grid coords, returning a cell/row/col attr or
    //   if multiple attr for the coords an attr that's merged, or the def attr
    wxSheetCellAttr GetGridAttr(const wxSheetCoords& coords) const { return GetAttr(coords, wxSHEET_AttrAny); }

    // Get a specific Cell/Row/Col attr for the coords in the grid
    //   if none set returns wxNullSheetCellAttr
    wxSheetCellAttr GetGridCellAttr(const wxSheetCoords& coords) const { return GetAttr(coords, wxSHEET_AttrCell); }
    wxSheetCellAttr GetGridRowAttr(int row) const { return GetAttr(wxSheetCoords(row,  0), wxSHEET_AttrRow); }
    wxSheetCellAttr GetGridColAttr(int col) const { return GetAttr(wxSheetCoords(0,  col), wxSHEET_AttrCol); }
    // Set a specific Cell/Row/Col attr for coords, row/col only apply to the grid
    void SetGridCellAttr(const wxSheetCoords& coords, const wxSheetCellAttr& attr) { SetAttr(coords, attr, wxSHEET_AttrCell); }
    void SetGridRowAttr(int row, const wxSheetCellAttr& attr) { SetAttr(wxSheetCoords(row,  0), attr, wxSHEET_AttrRow); }
    void SetGridColAttr(int col, const wxSheetCellAttr& attr) { SetAttr(wxSheetCoords(0,  col), attr, wxSHEET_AttrCol); }

    // Get the row/col/corner label attributes, if one is not set for the
    //  particular coords, returns the default one. (note: only one corner attr)
    //  if you want the particular attr use GetRow/ColLabelCellAttr
    wxSheetCellAttr GetRowLabelAttr(int row) const { return GetAttr(wxSheetCoords(row, -1), wxSHEET_AttrAny); }
    wxSheetCellAttr GetColLabelAttr(int col) const { return GetAttr(wxSheetCoords(-1, col), wxSHEET_AttrAny); }
    wxSheetCellAttr GetCornerLabelAttr() const     { return GetAttr(wxSheetCoords(-1,  -1), wxSHEET_AttrAny); }

    // Get a specific attr the row/col/corner label cell
    //   if none set returns wxNullSheetCellAttr
    wxSheetCellAttr GetRowLabelCellAttr(int row) const { return GetAttr(wxSheetCoords(row, -1), wxSHEET_AttrCell); }
    wxSheetCellAttr GetColLabelCellAttr(int col) const { return GetAttr(wxSheetCoords(-1, col), wxSHEET_AttrCell); }
    // Set a specific attribute for particular row/col/corner label cell
    void SetRowLabelCellAttr(int row, const wxSheetCellAttr& attr) { SetAttr(wxSheetCoords(row, -1), attr, wxSHEET_AttrCell); }
    void SetColLabelCellAttr(int col, const wxSheetCellAttr& attr) { SetAttr(wxSheetCoords(-1, col), attr, wxSHEET_AttrCell); }
    void SetCornerLabelAttr(const wxSheetCellAttr& attr)           { SetAttr(wxSheetCoords(-1,  -1), attr, wxSHEET_AttrCell); }

    // Get/Set default attributes for the areas (only one corner attr)
    //  For setting, wxSheetCellAttr::UpdateWith is called with the current default
    //  attr so you so need only set the values that you want to change.
    wxSheetCellAttr GetDefaultAttr(const wxSheetCoords& coords) const { return GetAttr(coords, wxSHEET_AttrDefault); }
    wxSheetCellAttr GetDefaultGridCellAttr() const { return GetAttr(wxGridCellSheetCoords, wxSHEET_AttrDefault); }
    wxSheetCellAttr GetDefaultRowLabelAttr() const { return GetAttr(wxRowLabelSheetCoords, wxSHEET_AttrDefault); }
    wxSheetCellAttr GetDefaultColLabelAttr() const { return GetAttr(wxColLabelSheetCoords, wxSHEET_AttrDefault); }
    void SetDefaultAttr(const wxSheetCoords& coords, const wxSheetCellAttr& attr) { SetAttr(coords, attr, wxSHEET_AttrDefault); }
    void SetDefaultGridCellAttr(const wxSheetCellAttr& attr) { SetAttr(wxGridCellSheetCoords, attr, wxSHEET_AttrDefault); }
    void SetDefaultRowLabelAttr(const wxSheetCellAttr& attr) { SetAttr(wxRowLabelSheetCoords, attr, wxSHEET_AttrDefault); }
    void SetDefaultColLabelAttr(const wxSheetCellAttr& attr) { SetAttr(wxColLabelSheetCoords, attr, wxSHEET_AttrDefault); }

    // These are convienience functions, if for example you want to subclass the
    //  table and modify and return default attr "on the fly" for each cell.
    //  Please use the standard methods if at all possible.
    const wxSheetCellAttr& DoGetDefaultGridAttr() const        { return GetSheetRefData()->m_defaultGridCellAttr; }
    const wxSheetCellAttr& DoGetDefaultRowLabelAttr() const    { return GetSheetRefData()->m_defaultRowLabelAttr; }
    const wxSheetCellAttr& DoGetDefaultColLabelAttr() const    { return GetSheetRefData()->m_defaultColLabelAttr; }
    const wxSheetCellAttr& DoGetDefaultCornerLabelAttr() const { return GetSheetRefData()->m_defaultCornerLabelAttr; }

    // Get/Set particular attributes for any type of cell/row/col anywhere
    //  The default is to get the attr val for type=wxSHEET_AttrAny meaning that
    //  it'll find a set attr first or return the default attr value as a last resort.
    //  For GetXXX you will receive an an error message if you specify a
    //  particular wxSHEET_AttrCell/Row/Col, but there isn't an attribute set
    const wxColour& GetAttrBackgroundColour( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    const wxColour& GetAttrForegroundColour( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    const wxFont&   GetAttrFont( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    int             GetAttrAlignment( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    int             GetAttrOrientation( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    int             GetAttrLevel( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    bool            GetAttrOverflow( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    bool            GetAttrOverflowMarker( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    bool            GetAttrShowEditor( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    bool            GetAttrReadOnly( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    wxSheetCellRenderer GetAttrRenderer( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;
    wxSheetCellEditor   GetAttrEditor( const wxSheetCoords& coords, wxSheetAttr_Type type = wxSHEET_AttrAny ) const;

    // Set attributes for a particular cell/row/col, relies on GetOrCreateAttr()
    //  so it creates and adds the attr to the attr provider if there wasn't one
    //  after setting the particular value.
    //  The type may be only be wxSHEET_AttrDefault/Cell/Row/Col
    void SetAttrBackgroundColour( const wxSheetCoords& coords, const wxColour& colour, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrForegroundColour( const wxSheetCoords& coords, const wxColour& colour, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrFont( const wxSheetCoords& coords, const wxFont& font, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrAlignment( const wxSheetCoords& coords, int align, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrOrientation( const wxSheetCoords& coords, int orientation, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrLevel( const wxSheetCoords& coords, int level, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrOverflow( const wxSheetCoords& coords, bool allow, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrOverflowMarker( const wxSheetCoords& coords, bool draw_marker, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrShowEditor( const wxSheetCoords& coords, bool show_editor, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrReadOnly( const wxSheetCoords& coords, bool isReadOnly, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrRenderer( const wxSheetCoords& coords, const wxSheetCellRenderer &renderer, wxSheetAttr_Type type = wxSHEET_AttrCell );
    void SetAttrEditor( const wxSheetCoords& coords, const wxSheetCellEditor &editor, wxSheetAttr_Type type = wxSHEET_AttrCell );

    // helper functions that use SetColAttr to set renderer type
    // set the format for the data in the column: default is string
    void SetColFormatBool(int col)   { SetColFormatCustom(col, wxSHEET_VALUE_BOOL); }
    void SetColFormatNumber(int col) { SetColFormatCustom(col, wxSHEET_VALUE_NUMBER); }
    void SetColFormatFloat(int col, int width = -1, int precision = -1);
    void SetColFormatCustom(int col, const wxString& typeName);

    // ------------------------------------------------------------------------
    // Get/Set cell, row, col, and corner label values

    // *** To completely override value handling you may provide alternate
	// *** GetCellValue and SetCellValue

    // Get/Set cell value, uses coords = -1 notation for row/col/corner labels
    virtual wxString GetCellValue( const wxSheetCoords& coords );
    virtual void SetCellValue( const wxSheetCoords& coords, const wxString& value );

    // Is this cell empty, see wxSheetTable
    virtual bool HasCellValue( const wxSheetCoords& coords );

    wxString GetRowLabelValue( int row ) { return GetCellValue(wxSheetCoords(row, -1)); }
    wxString GetColLabelValue( int col ) { return GetCellValue(wxSheetCoords(-1, col)); }
    void     SetRowLabelValue( int row, const wxString& value ) { SetCellValue(wxSheetCoords(row, -1), value); }
    void     SetColLabelValue( int col, const wxString& value ) { SetCellValue(wxSheetCoords(-1, col), value); }

    wxString GetCornerLabelValue() { return GetCellValue(wxSheetCoords(-1, -1)); }
    void     SetCornerLabelValue(const wxString& value) { SetCellValue(wxSheetCoords(-1, -1), value); }

    // ------------------------------------------------------------------------
    // Register mapping between data types to Renderers/Editors

    // I don't fully understand the reasoning for these, it's some sort of
    //  string registry for the editors and renderers.
    //  It's not clear to me why this is useful. - John Labenski

    void RegisterDataType( const wxString& typeName,
                           const wxSheetCellRenderer& renderer,
                           const wxSheetCellEditor& editor );

    virtual wxSheetCellEditor   GetDefaultEditorForType(const wxString& typeName) const;
    virtual wxSheetCellRenderer GetDefaultRendererForType(const wxString& typeName) const;

    // FIXME what is the point of these?
    virtual wxSheetCellEditor   GetDefaultEditorForCell(const wxSheetCoords& coords) const;
    virtual wxSheetCellRenderer GetDefaultRendererForCell(const wxSheetCoords& coords) const;

    // ------------------------------------------------------------------------
    // Cursor movement and visibility functions

    // Check to see if a cell is either wholly visible (the default arg)
    //   or at least partially visible, uses -1 notation for labels
    bool IsCellVisible( const wxSheetCoords& coords, bool wholeCellVisible = true ) const;
    bool IsRowVisible( int row, bool wholeRowVisible = true ) const;
    bool IsColVisible( int col, bool wholeColVisible = true ) const;
    // Make this cell visible, uses -1 notation, will not unhide label windows
    void MakeCellVisible( const wxSheetCoords& coords );

    // Get/Set cursor cell, this is the "current" cell where a highlight is drawn.
    //  The cursor only applies to the grid cells.
    const wxSheetCoords& GetGridCursorCell() const { return GetSheetRefData()->m_cursorCoords; }
    int  GetGridCursorRow() const { return GetGridCursorCell().GetRow(); }
    int  GetGridCursorCol() const { return GetGridCursorCell().GetCol(); }
    void SetGridCursorCell( const wxSheetCoords& coords );

    // These are simplified methods for moving the cursor, mostly used internally
    //  for handling key press movements.
    bool MoveCursorUp( bool expandSelection )    { return DoMoveCursor(wxSheetCoords(-1, 0), expandSelection); }
    bool MoveCursorDown( bool expandSelection )  { return DoMoveCursor(wxSheetCoords( 1, 0), expandSelection); }
    bool MoveCursorLeft( bool expandSelection )  { return DoMoveCursor(wxSheetCoords( 0,-1), expandSelection); }
    bool MoveCursorRight( bool expandSelection ) { return DoMoveCursor(wxSheetCoords( 0, 1), expandSelection); }
    bool MoveCursorUpBlock( bool expandSelection )    { return DoMoveCursorBlock(wxSheetCoords(-1, 0), expandSelection); }
    bool MoveCursorDownBlock( bool expandSelection )  { return DoMoveCursorBlock(wxSheetCoords( 1, 0), expandSelection); }
    bool MoveCursorLeftBlock( bool expandSelection )  { return DoMoveCursorBlock(wxSheetCoords( 0,-1), expandSelection); }
    bool MoveCursorRightBlock( bool expandSelection ) { return DoMoveCursorBlock(wxSheetCoords( 0, 1), expandSelection); }
    bool MoveCursorUpPage( bool expandSelection )   { return DoMoveCursorUpDownPage(true, expandSelection); }
    bool MoveCursorDownPage( bool expandSelection ) { return DoMoveCursorUpDownPage(false, expandSelection); }

    virtual bool DoMoveCursor( const wxSheetCoords& relCoords, bool expandSelection );
    virtual bool DoMoveCursorBlock( const wxSheetCoords& relDir, bool expandSelection );
    virtual bool DoMoveCursorUpDownPage( bool page_up, bool expandSelection );

    // ------------------------------------------------------------------------
    // Cell/Row/Col selection and deselection, you can only select grid cells.

    // Note: A selection to the # of rows/cols means that the whole row/col is
    //   selected. Otherwise the right/bottom is rows/cols - 1, ie. contained
    //   within the actual number of cells.
    //   If sendEvt a wxEVT_SHEET_RANGE_SELECTED is sent, the SELECTING event
    //     should have been sent by the caller and if vetoed not call these.
    //   All functions (De)Select/Row/Col/Cell, SelectAll go to (De)SelectBlock.
    //   ClearSelection deselects everything and sends a single event with
    //   wxSheetBlock(0,0,rows,cols) to imply everything is cleared.

    // *** To completely override selection handling you may provide alternate
    // *** HasSelection, IsBlockSelected, SelectBlock, DeSelectBlock, and ClearSelection.

    // Is there any selection, if selecting, includes the active selection block
    //   which is not yet part of underlying selection system
    virtual bool HasSelection(bool selecting = true) const;
    // Are these coords within either the selecting block or selection
    virtual bool IsCellSelected( const wxSheetCoords& coords ) const;
    virtual bool IsRowSelected( int row ) const;
    virtual bool IsColSelected( int col ) const;
    virtual bool IsBlockSelected( const wxSheetBlock& block ) const;
    // Are we currently in the middle of a selection
    bool IsSelecting() const { return !GetSelectingBlock().IsEmpty(); }

    void SetSelectionMode(wxSheetSelectionMode_Type selmode);
    int  GetSelectionMode() const { return GetSheetRefData()->m_selectionMode; }
    bool HasSelectionMode(int mode) { return (GetSelectionMode() == mode) || ((GetSelectionMode() & mode) != 0); }

    // return the input block so it's appropriate for the selection mode
    //  if sel_mode == -1 then use GetSelectionMode.
    virtual wxSheetBlock FixSelectionBlock(const wxSheetBlock& block,
                                           int sel_mode = -1) const;

    virtual bool SelectRow( int row, bool addToSelected = false, bool sendEvt = false );
    virtual bool SelectRows( int rowTop, int rowBottom, bool addToSelected = false, bool sendEvt = false );
    virtual bool SelectCol( int col, bool addToSelected = false, bool sendEvt = false );
    virtual bool SelectCols( int colLeft, int colRight, bool addToSelected = false, bool sendEvt = false );
    virtual bool SelectCell( const wxSheetCoords& coords, bool addToSelected = false, bool sendEvt = false );
    virtual bool SelectBlock( const wxSheetBlock& block, bool addToSelected = false, bool sendEvt = false );
    // selects everything to numRows, numCols
    virtual bool SelectAll(bool sendEvt = false);

    virtual bool DeselectRow( int row, bool sendEvt = false );
    virtual bool DeselectRows( int rowTop, int rowBottom, bool sendEvt = false );
    virtual bool DeselectCol( int col, bool sendEvt = false );
    virtual bool DeselectCols( int colLeft, int colRight, bool sendEvt = false );
    virtual bool DeselectCell( const wxSheetCoords& coords, bool sendEvt = false );
    virtual bool DeselectBlock( const wxSheetBlock& block, bool sendEvt = false );
    // clears selection, single deselect event numRows, numCols
    virtual bool ClearSelection(bool send_event = false);

    // toggle the selection of a single cell, row, or col
    // addToSelected applies to a selection only, ignored if a deselection
    virtual bool ToggleCellSelection( const wxSheetCoords& coords,
                                      bool addToSelected = false, bool sendEvt = false );
    virtual bool ToggleRowSelection( int row, bool addToSelected = false, bool sendEvt = false );
    virtual bool ToggleColSelection( int col, bool addToSelected = false, bool sendEvt = false );

    // Get a pointer to the selection mechanism. You are free to do what you
    //  want with it, do a ForceRefresh to update the grid when done.
    wxSheetSelection* GetSelection() const { return GetSheetRefData()->m_selection; }

    // During a selection this is the selecting block, else empty
    const wxSheetBlock& GetSelectingBlock() const { return GetSheetRefData()->m_selectingBlock; }
    const wxSheetCoords& GetSelectingAnchor() const { return GetSheetRefData()->m_selectingAnchor; }

    // These are internal use functions to highlight a block during mouse
    //  dragging or keyboard selecting
    void SetSelectingBlock(const wxSheetBlock& selectingBlock) { GetSheetRefData()->m_selectingBlock = selectingBlock; }
    void SetSelectingAnchor(const wxSheetCoords& selectingAnchor) { GetSheetRefData()->m_selectingAnchor = selectingAnchor; }

    // while selecting set and draw m_selectingBlock highlight and clear up last
    virtual void HighlightSelectingBlock( const wxSheetBlock& selectingBlock );
    void HighlightSelectingBlock( const wxSheetCoords& cornerCell,
                                  const wxSheetCoords& otherCell )
        { HighlightSelectingBlock(wxSheetBlock(cornerCell, otherCell)); }

    // ------------------------------------------------------------------------
    // Copy/Paste functionality for strings (Experimental)

    // Copy the current selection using CopyCurrentSelectionToInternalSelection then
    //  to the wxClipboard using CopyInternalSelectionToClipboard
    bool CopyCurrentSelectionToClipboard(bool copy_cursor = true,
                                         const wxChar& colSep = wxT('\t'));
    // Copy the current selection to an internal copied selection mechanism
    //  storing both the positions and values of the selected cells, if no
    //  selection and copy_cursor then just copy the cursor value
    bool CopyCurrentSelectionToInternalSelection(bool copy_cursor = true);
    // Copy the internal selection to the wxClipboard as both a string using
    //  colSep to separate columns and as an internal representation for
    //  pasting back into the wxSheet.
    bool CopyInternalSelectionToClipboard(const wxChar& colSep = wxT('\t'));
    // Returns the internal selection as a suitable string to be put into the clipboard.
    //   uses colSep for cols and \n for rows, called by CopyInternalSelectionToClipboard
    wxString CopyInternalSelectionToString(const wxChar& colSep = wxT('\t'));

    // Copies the given string (perhaps from the clipboard) to the internal copied
    //   selection uses colSep for cols and \n for rows, used by PasteFromClipboard
    bool CopyStringToInternalSelection(const wxString& value, const wxChar& colSep = wxT('\t'));

    // Tries to get the clipboard data as wxSheet's clipboard data
    // representation else use CopyStringToInternalSelection to convert a string
    //  using colSep as the column separator and \n as row separator.
    //  If coords are wxNullSheetCoords, use current cursor position.
    bool PasteFromClipboard(const wxSheetCoords &topLeft = wxNullSheetCoords,
                            const wxChar& colSep = wxT('\t'));
    // Paste the internal copied selection at the topLeft coords or if
    //  topLeft = wxNullSheetCoords then if IsSelection use the upper right of
    //  the current selection and only paste into currently selected cells.
    //  If no selection the the cursor is the topLeft cell.
    virtual bool PasteInternalCopiedSelection(const wxSheetCoords &topLeft = wxNullSheetCoords);
    // Are the cells being pasted right now, use this in the table's
    //    SetCellValue and AppendXXX to differentiate between a user typing
    bool CurrentlyPasting() const { return GetSheetRefData()->m_pasting; }

    // ------------------------------------------------------------------------
    // Edit enable/disabling functions

    // Are the cells in the enum wxSheetCell_Type editable?
    int IsAreaEditable( int cell_type ) const { return (cell_type & GetSheetRefData()->m_editable) != 0; }
	// Get the area editable mask of areas that are editable
    int GetAreaEditable() const { return GetSheetRefData()->m_editable; }
    // Enable editing of the cells in the enum wxSheetCell_Type, can use masks or
	// wxSHEET_CELL_ANY for all editable, or wxSHEET_CELL_NONE for none
    void SetAreaEditable( int cell_type );

    // ------------------------------------------------------------------------
    // Edit control functions (mostly used internally)

    // enable and show the editor control at the coords, returns sucess, ie. !vetoed
    bool EnableCellEditControl( const wxSheetCoords& coords );
    // hide and disable the editor and save the value if save_value, returns sucess, ie. !vetoed
    bool DisableCellEditControl( bool save_value );
    // is this cell valid and editable
    bool CanEnableCellControl(const wxSheetCoords& coords) const;
    // is the cell editor created (may not be shown though)
    bool IsCellEditControlCreated() const;
    // is the cell editor valid and shown
    bool IsCellEditControlShown() const;

    // Create and show the appropriate editor at the EnableCellEditControl coords
    //  this is called internally by EnableCellEditControl, but if you call
    //  HideCellEditControl and if IsCellEditControlCreated then you can reshow
    //  it with this, returns sucess
    bool ShowCellEditControl();
    // Hide the editor, doesn't destroy it (use DisableCellEditControl)
    //  check if IsCellEditControlShown first, returns sucess
    bool HideCellEditControl();
    // Save the value of the editor, check IsCellEditControlEnabled() first
    void SaveEditControlValue();

    // Get the current editor, !Ok() if !IsCellEditControlCreated()
    const wxSheetCellEditor& GetEditControl() const { return GetSheetRefData()->m_cellEditor; }
    // These are the coords of the editor, check IsCellEditControlCreated before using
    const wxSheetCoords& GetEditControlCoords() const { return GetSheetRefData()->m_cellEditorCoords; }

    // ------------------------------------------------------------------------
    // Drawing functions

    // Code that does a lot of grid modification can be enclosed
    // between BeginBatch() and EndBatch() calls to avoid screen flicker
    // EndBatch's refresh = false will not refresh when batchCount is 0
    void BeginBatch() { GetSheetRefData()->m_batchCount++; }
    void EndBatch(bool refresh=true);
    int  GetBatchCount() const { return GetSheetRefData()->m_batchCount; }

    // Use ForceRefresh, rather than wxWindow::Refresh(), to force an
    // immediate repainting of the grid. No effect if GetBatchCount() > 0
    // This function is necessary because wxSheet has a minimal OnPaint()
    // handler to reduce screen flicker.
    void ForceRefresh() { BeginBatch(); EndBatch(); }

    // *** Use these redrawing functions to ensure refed sheets are redrawn ***

    // Refresh a single cell, can also draw cells for labels using -1 notation
    // does nothing if cell !visible, or GetBatchCount != 0
    // if single_cell then literally draw only the single cell, else draw the
    // cell to left in case the overflow marker needs to be drawn and the
    // cells to the right in case this cell overflows.
    void RefreshCell(const wxSheetCoords& coords, bool single_cell = true);
    // Refresh a block of cells in any/all of the windows by chopping up the block,
    //   uses -1 notation to refresh labels
    void RefreshBlock(const wxSheetBlock& block);
    // Refresh a single row, row = -1 refreshes all col labels,
    // does nothing if row !visible, or GetBatchCount != 0
    void RefreshRow(int row);
    // Refresh a single col, col = -1 refreshes all row labels,
    // does nothing if col !visible, or GetBatchCount != 0
    void RefreshCol(int col);
    // Refresh is called using a rect surrounding the block
    // does nothing if block IsEmpty, !visible, or GetBatchCount != 0
    void RefreshGridCellBlock( const wxSheetBlock& block );
    // After SetAttr call this can appropriately refresh the wxSheet areas
    void RefreshAttrChange(const wxSheetCoords& coords, wxSheetAttr_Type type);

    // ************************************************************************
    // Drawing implementation - not for general use

    // Refresh an area of the window that calculates the smaller rects for
    //  each individual window (row/col/corner...) and calls Refresh(subRect)
    //  The rect is the logical rect, not the scrolled device rect
    virtual void Refresh(bool eraseb = true, const wxRect* rect = NULL);

    // These directly call wxWindow::Refresh for the appropriate windows
    //   The input rect doesn't have to be clipped to the visible window since
    //   this function takes care of that, but it should be in client coords.
    void RefreshGridWindow(bool eraseb = true, const wxRect* rect = NULL);
    void RefreshRowLabelWindow(bool eraseb = true, const wxRect* rect = NULL);
    void RefreshColLabelWindow(bool eraseb = true, const wxRect* rect = NULL);
    void RefreshCornerLabelWindow(bool eraseb = true, const wxRect* rect = NULL);

    // Don't use these if you plan to use the splitter since they only act
    //   on this sheet.

    // These functions are called by the OnPaint handler of these windows
    //   use these to add "extra touches" before or after redrawing.
    //   The dc should be prepared before calling these.
    virtual void PaintGridWindow( wxDC& dc, const wxRegion& reg );
    virtual void PaintRowLabelWindow( wxDC& dc, const wxRegion& reg );
    virtual void PaintColLabelWindow( wxDC& dc, const wxRegion& reg );
    virtual void PaintCornerLabelWindow( wxDC& dc, const wxRegion& reg );
    virtual void PaintSheetWindow( wxDC& dc, const wxRegion& reg );

    // draws a bunch of blocks of grid cells onto the given DC
    virtual void DrawGridCells( wxDC& dc, const wxSheetSelection& blockSel );
    // Draw the area below and to right of grid up to scrollbars
    virtual void DrawGridSpace( wxDC& dc );
    // draw the border around a single cell
    virtual void DrawCellBorder( wxDC& dc, const wxSheetCoords& coords );
    // Draw all the grid lines in the region
    virtual void DrawAllGridLines( wxDC& dc, const wxRegion& reg );
    // Draw a single cell
    virtual void DrawCell( wxDC& dc, const wxSheetCoords& coords );
    // Calls DrawCursorCellHighlight if contained within this selection
    virtual void DrawCursorHighlight( wxDC& dc, const wxSheetSelection& blockSel );
    // Draw the cursor cell highlight
    virtual void DrawCursorCellHighlight(wxDC& dc, const wxSheetCellAttr &attr);

    // draw wxSheetRowLabelWindow labels
    virtual void DrawRowLabels( wxDC& dc, const wxArrayInt& rows );
    // draw wxSheetColLabelWindow labels
    virtual void DrawColLabels( wxDC& dc, const wxArrayInt& cols );
    // draw wxSheetCornerLabelWindow label
    virtual void DrawCornerLabel( wxDC& dc );

    // Draw the row/col resizing marker and if newDragPos != -1, set the
    //  new position of the marker
    virtual void DrawRowColResizingMarker( int newDragPos = -1 );

    // Draw the splitter button in the rectangle
    virtual void DrawSplitterButton(wxDC &dc, const wxRect& rect);

    // Calculate the Row/ColLabels and Cells exposed for the wxRegion
    //   returns false if none, used for redrawing windows
    bool CalcRowLabelsExposed( const wxRegion& reg, wxArrayInt& rows ) const;
    bool CalcColLabelsExposed( const wxRegion& reg, wxArrayInt& cols ) const;
    bool CalcCellsExposed( const wxRegion& reg, wxSheetSelection& blockSel ) const;
    int  FindOverflowCell( const wxSheetCoords& coords, wxDC& dc );

    // helper drawing functions
    void DrawTextRectangle( wxDC& dc, const wxString& value, const wxRect& rect,
                            int alignment = wxALIGN_LEFT|wxALIGN_TOP,
                            int textOrientation = wxHORIZONTAL );

    void DrawTextRectangle( wxDC& dc, const wxArrayString& lines, const wxRect&,
                            int alignment = wxALIGN_LEFT|wxALIGN_TOP,
                            int textOrientation = wxHORIZONTAL );

    // Split string by '\n' and add to array, returning the number of lines
    //  returns 0 for empty string.
    int StringToLines( const wxString& value, wxArrayString& lines ) const;
    // Get the size of the lines drawn horizontally, returns true if size > 0
    bool GetTextBoxSize( wxDC& dc, const wxArrayString& lines,
                         long *width, long *height ) const;

    // ------------------------------------------------------------------------
    // Geometry utility functions, pixel <-> cell etc

    // Note that all of these functions work with the logical coordinates of
    // grid cells and labels so you will need to convert from device
    // coordinates for mouse events etc.
    // clipToMinMax means that the return value will be within the grid cells
    // if !clipToMinMax and out of bounds it returns -1.
    // Use ContainsGridXXX to verify validity, -1 doesn't mean label
    wxSheetCoords XYToGridCell( int x, int y, bool clipToMinMax = false ) const;
    int YToGridRow( int y, bool clipToMinMax = false ) const;
    int XToGridCol( int x, bool clipToMinMax = false ) const;

    // return the row/col number that the x/y coord is near the edge of, or
    // -1 if not near an edge. edge_size is +- pixels to cell edge
    // Use ContainsGridXXX to verify validity, -1 doesn't mean label
    int YToEdgeOfGridRow( int y, int edge_size = WXSHEET_LABEL_EDGE_ZONE ) const;
    int XToEdgeOfGridCol( int x, int edge_size = WXSHEET_LABEL_EDGE_ZONE ) const;

    // Get a rect bounding the cell, handles spanning cells and the label
    //  windows using the -1 notation, getDeviceRect calls CalcScrolledRect
    wxRect CellToRect( const wxSheetCoords& coords, bool getDeviceRect = false ) const;
    // Get a rect bounding the block, handles label windows using the -1 notation,
    //  getDeviceRect calls CalcScrolledRect
    wxRect BlockToRect( const wxSheetBlock& block, bool getDeviceRect = false ) const;

    // Expand the block by unioning with intersecting spanned cells
    wxSheetBlock ExpandSpannedBlock( const wxSheetBlock& block ) const;

    // Convert the block of cells into a wxRect in device coords, expands the
    //  block to contain spanned cells if expand_spanned.
    //  These functions do handle label cells, but if you span the block from a label
    //  into the grid then the rect will overlap the windows, probably not what you want.
    wxRect BlockToDeviceRect( const wxSheetBlock& block, bool expand_spanned = true ) const;
    wxRect BlockToLogicalRect( const wxSheetBlock& block, bool expand_spanned = true ) const;

    // Convert the rect in pixels into a block of cells for the grid
    //   if wholeCell then only include cells in the block that are
    //   wholly contained by the rect
    wxSheetBlock LogicalGridRectToBlock(const wxRect &rect, bool wholeCell = false) const;

    // get a block containing all the currently (partially/fully) visible cells
    wxSheetBlock GetVisibleGridCellsBlock(bool wholeCellVisible = false) const;

    // Align the size of an object inside the rect using wxALIGN enums
    //   if inside then align it to the left if it would have overflown
    //   always pins size to left hand side
    static wxPoint AlignInRect( int align, const wxRect& rect, const wxSize& size, bool inside=true );

    // ------------------------------------------------------------------------
    // Scrolling for the window, everything is done with pixels
    //   there is no need for scroll units and they only cause sizing problems

    // Get the scrolled origin of the grid in pixels
    const wxPoint& GetGridOrigin() const { return m_gridOrigin; }
    // Set the absolute scrolled origin of the grid window in pixels
    //  this checks validity and ensures proper positioning.
    //  Use x or y = -1 to not change the origin in the x or y direction
    //  Unless setting from a scrollbar event use adjustScrollBars=true
    virtual void SetGridOrigin(int x, int y, bool adjustScrollBars = true, bool sendEvt=false);
    void SetGridOrigin(const wxPoint& pt, bool adjustScrollBars = true, bool sendEvt=false)
        { SetGridOrigin(pt.x, pt.y, adjustScrollBars, sendEvt); }

    // Get the virtual size of the grid in pixels, includes extra width/height,
    //   but does not include the row/col labels width/height.
    wxSize GetGridVirtualSize(bool add_margin=true) const;

    // Get the full size of the sheet which is the width/height of the row/col
    //   labels + the virtual size of the grid.
    wxSize GetSheetVirtualSize(bool add_margin=true) const;

    // Get the extent of the grid, which is the max of the virtual size and
    //  actual grid window size. Therefore, this may be larger than the virtual
    //  size if the grid is smaller than the containing window.
    wxSize GetGridExtent() const;

    // Same as wxScrolledWindow Calc(Un)ScrolledPosition
    void CalcScrolledPosition(int x, int y, int *xx, int *yy) const
        { if (xx) *xx = x - m_gridOrigin.x; if (yy) *yy = y - m_gridOrigin.y; }
    void CalcUnscrolledPosition(int x, int y, int *xx, int *yy) const
        { if (xx) *xx = x + m_gridOrigin.x; if (yy) *yy = y + m_gridOrigin.y; }
    wxPoint CalcScrolledPosition(const wxPoint& pt) const   { return pt - m_gridOrigin; }
    wxPoint CalcUnscrolledPosition(const wxPoint& pt) const { return pt + m_gridOrigin; }

    // returns the scrolled position of the rect, logical -> device coords
    wxRect CalcScrolledRect(const wxRect &r) const
        { return wxRect(r.x-m_gridOrigin.x, r.y-m_gridOrigin.y, r.width, r.height); }
    // returns the unscrolled position of the rect, device -> logical coords
    wxRect CalcUnscrolledRect(const wxRect &r) const
        { return wxRect(r.x+m_gridOrigin.x, r.y+m_gridOrigin.y, r.width, r.height); }

    // Adjust the scrollbars to match the size/origin of the grid window
    //   call this after SetScrollBarMode
    virtual void AdjustScrollbars(bool calc_win_sizes = true);

    enum SB_Mode
    {
        SB_AS_NEEDED    = 0x0,  // Show the scrollbars as needed
        SB_HORIZ_NEVER  = 0x1,  // Never show horiz scrollbar, even if needed
        SB_VERT_NEVER   = 0x2,  // Never show vert scrollbar, even if needed
        SB_NEVER        = SB_HORIZ_NEVER | SB_VERT_NEVER,
        SB_HORIZ_ALWAYS = 0x4,  // Always show horiz scrollbar
        SB_VERT_ALWAYS  = 0x8,  // Always show vert scrollbar
        SB_ALWAYS       = SB_HORIZ_ALWAYS | SB_VERT_ALWAYS,

        SB_HORIZ_MASK   = SB_HORIZ_NEVER|SB_HORIZ_ALWAYS,
        SB_VERT_MASK    = SB_VERT_NEVER|SB_VERT_ALWAYS
    };

    int GetScrollBarMode() const { return m_scrollBarMode; }
    // After setting the scrollbar mode, you must call AdjustScrollbars(true)
    //   for the setting to take effect.
    void SetScrollBarMode(int mode) { m_scrollBarMode = mode; }
    void SetHorizontalScrollBarMode(int mode)
        { m_scrollBarMode &= (~SB_HORIZ_MASK); m_scrollBarMode |= mode; }
    void SetVerticalScrollBarMode(int mode)
        { m_scrollBarMode &= (~SB_VERT_MASK);  m_scrollBarMode |= mode; }

    bool NeedsVerticalScrollBar()   const { return GetGridVirtualSize().y > m_gridWin->GetSize().y; }
    bool NeedsHorizontalScrollBar() const { return GetGridVirtualSize().x > m_gridWin->GetSize().x; }

    // SetDeviceOrigin for the wxDC as appropriate for these windows

    virtual void PrepareGridDC( wxDC& dc );
    virtual void PrepareRowLabelDC( wxDC& dc );
    virtual void PrepareColLabelDC( wxDC& dc );

    // ------------------------------------------------------------------------
    // Splitting of the grid window - note that the sheet does not split at all
    //   and that only a wxEVT_SHEET_SPLIT_BEGIN event is sent to notify the
    //   parent that splitting should take place, see wxSheetSplitter.
    //   The "splitter" is just two small rectangles at the top of the vertical
    //   scrollbar and right of the horizontal scrollbar. They're only shown
    //   when the scrollbars are shown and if splitting is enabled.
    //   Call CalcWindowSizes after setting to update the display.

    // Are the splitter buttons enabled to be shown as necessary
    bool GetEnableSplitVertically()   const { return m_enable_split_vert; }
    bool GetEnableSplitHorizontally() const { return m_enable_split_horiz; }
    // Enable or disable showing the splitter buttons
    void EnableSplitVertically(bool can_split)   { m_enable_split_vert = can_split; }
    void EnableSplitHorizontally(bool can_split) { m_enable_split_horiz = can_split; }

    // ------------------------------------------------------------------------
    // implementation

    // helper function to set only the horiz or vert component of orig_align
    //   returns modified alignment, doesn't modify any bits not in wxAlignment
    //   use -1 for hAlign/vAlign to not modify that direction
    static int SetAlignment(int orig_align, int hAlign, int vAlign);

    // Do any of the windows of the wxSheet have the focus?
    bool HasFocus() const;

    // Accessors for component windows
    wxSheetChildWindow* GetGridWindow()        const { return m_gridWin; }
    wxSheetChildWindow* GetRowLabelWindow()    const { return m_rowLabelWin; }
    wxSheetChildWindow* GetColLabelWindow()    const { return m_colLabelWin; }
    wxSheetChildWindow* GetCornerLabelWindow() const { return m_cornerLabelWin; }
    // Get the window with these coords, uses -1 notation
    wxWindow* GetWindowForCoords( const wxSheetCoords& coords ) const;

    // ------ event handlers
    void OnMouse( wxMouseEvent& event );
    void OnMouseWheel( wxMouseEvent& event );

    virtual void ProcessSheetMouseEvent( wxMouseEvent& event );
    virtual void ProcessRowLabelMouseEvent( wxMouseEvent& event );
    virtual void ProcessColLabelMouseEvent( wxMouseEvent& event );
    virtual void ProcessCornerLabelMouseEvent( wxMouseEvent& event );
    virtual void ProcessGridCellMouseEvent( wxMouseEvent& event );

    void OnScroll( wxScrollEvent& event );

    // End the row/col dragging, returns true if width/height have changed
    bool DoEndDragResizeRowCol();

    // ------ control types
    enum
    {
        wxSHEET_TEXTCTRL = 2100,
        wxSHEET_CHECKBOX,
        wxSHEET_CHOICE,
        wxSHEET_COMBOBOX
    };

    enum
    {
        ID_HORIZ_SCROLLBAR = 1,
        ID_VERT_SCROLLBAR,
        ID_MOUSE_DRAG_TIMER,
        ID_GRID_WINDOW,
        ID_ROW_LABEL_WINDOW,
        ID_COL_LABEL_WINDOW,
        ID_CORNER_LABEL_WINDOW
    };

    virtual void CalcWindowSizes(bool adjustScrollBars = true);
    virtual void Fit() { AutoSize(); } // overridden wxWindow methods

    // Get the ref counted data the sheet uses, *please* try to not access this
    //  directly if a function already exists to do what you want.
    wxSheetRefData* GetSheetRefData() const { return (wxSheetRefData*)GetRefData(); }

    // Create and send wxSheetXXXEvent depending on type and fill extra data
    //   from a wxKeyEvent or wxMouseEvent (if NULL all keydown are set false)
    // returns EVT_VETOED/SKIPPED/CLAIMED
    enum
    {
        EVT_VETOED  = -1,  // veto was called on the event
        EVT_SKIPPED = 0,   // no evt handler found or evt was Skip()ed
        EVT_CLAIMED = 1    // event was handled and not Skip()ed
    };
    int SendEvent( const wxEventType type, const wxSheetCoords& coords,
                   wxEvent* mouseOrKeyEvt = NULL,
                   const wxString& cmdString = wxEmptyString, int cmd_int = 0);
    int SendCellSizeEvent( const wxEventType type, const wxSheetCoords& coords,
                           int new_size, wxEvent* mouseOrKeyEvt = NULL );
    int SendRangeSelectEvent( const wxEventType type, const wxSheetBlock& block,
                              bool selecting, bool add, wxEvent* mouseOrKeyEvt = NULL );
    int SendEditorCreatedEvent( const wxEventType type, const wxSheetCoords& coords,
                                wxWindow* ctrl );

    // Just send the event returning EVT_VETOED/SKIPPED/CLAIMED
    int DoSendEvent( wxSheetEvent* event );

    enum MouseCursorMode
    {
        WXSHEET_CURSOR_SELECT_CELL = 0x0001,
        WXSHEET_CURSOR_SELECT_ROW  = 0x0002,
        WXSHEET_CURSOR_SELECT_COL  = 0x0004,
        WXSHEET_CURSOR_SELECTING   = WXSHEET_CURSOR_SELECT_CELL|WXSHEET_CURSOR_SELECT_ROW|WXSHEET_CURSOR_SELECT_COL,
        WXSHEET_CURSOR_RESIZE_ROW  = 0x0008,
        WXSHEET_CURSOR_RESIZE_COL  = 0x0010,
        WXSHEET_CURSOR_RESIZING    = WXSHEET_CURSOR_RESIZE_ROW|WXSHEET_CURSOR_RESIZE_COL,
        WXSHEET_CURSOR_SPLIT_VERTICAL   = 0x0020,
        WXSHEET_CURSOR_SPLIT_HORIZONTAL = 0x0040,
        WXSHEET_CURSOR_SPLITTING        = WXSHEET_CURSOR_SPLIT_VERTICAL|WXSHEET_CURSOR_SPLIT_HORIZONTAL
    };
    // Set the m_mouseCursor for the wxCursor and m_mouseCursorMode for behavior
    // you should always use it and not set m_mouseCursor[Mode] directly!
    void SetMouseCursorMode( MouseCursorMode mode, wxWindow *win );
    // Get the mouse cursor mode, &ed with mask, default returns original value
    int GetMouseCursorMode(int mask = ~0) const { return (m_mouseCursorMode & mask); }
    // Is the mouse cursor in the mode?
    bool HasMouseCursorMode(int mode) const { return GetMouseCursorMode(mode) != 0; }

    // Set the window that has capture, releases the previous one if necessary
    // always use this, set with NULL to release mouse
    void SetCaptureWindow( wxWindow *win );
    wxWindow *GetCaptureWindow() const { return m_winCapture; }

protected:

    bool DoUpdateRows(size_t row, int numRows, int update = wxSHEET_UpdateAll );
    bool DoUpdateCols(size_t col, int numCols, int update = wxSHEET_UpdateAll );

    virtual wxSize DoGetBestSize() const;
    void OnSize( wxSizeEvent& event );

    wxWindow *m_winCapture;   // the window that captured the mouse (don't use!)

    wxSheetChildWindow *m_gridWin;
    wxSheetChildWindow *m_rowLabelWin;
    wxSheetChildWindow *m_colLabelWin;
    wxSheetChildWindow *m_cornerLabelWin;
    wxScrollBar        *m_horizScrollBar;
    wxScrollBar        *m_vertScrollBar;

    wxPoint m_gridOrigin;     // origin of the gridWin in pixels
    int     m_scrollBarMode;

    bool    m_keySelecting;

    // cell attribute cache
    wxSheetCellAttr m_cacheAttr;
    wxSheetCoords   m_cacheAttrCoords;
    int             m_cacheAttrType;

    // invalidates the attribute cache
    void ClearAttrCache();
    // adds an attribute to cache
    void CacheAttr(const wxSheetCoords& coords, const wxSheetCellAttr &attr,
                   wxSheetAttr_Type type ) const;
    // looks for an attr in cache, returns true if found
    bool LookupAttr(const wxSheetCoords& coords, wxSheetAttr_Type type,
                    wxSheetCellAttr &attr) const;

    bool m_inOnKeyDown;
    bool m_resizing;

    enum KeyModifiers
    {
        NO_MODIFIERS = 0,
        CTRL_DOWN    = 0x0001,
        SHIFT_DOWN   = 0x0002,
        ALT_DOWN     = 0x0004,
        META_DOWN    = 0x0008  // meta is numlock in GTK so it's ignored
    };
    int GetKeyModifiers(wxEvent *mouseOrKeyEvent) const;

    MouseCursorMode m_mouseCursorMode;
    MouseCursorMode m_mouseCursor;

    int     m_dragLastPos;
    int     m_dragRowOrCol;
    bool    m_isDragging;
    wxPoint m_startDragPos;
    wxPoint m_mousePos;

    bool    m_waitForSlowClick;

    // mouse timer for smooth scrolling when selecting or resizing off window
    void StartMouseTimer();
    void StopMouseTimer();
    void OnMouseTimer( wxTimerEvent &event );
    wxTimer *m_mouseTimer;

    bool   m_enable_split_vert;
    bool   m_enable_split_horiz;
    wxRect m_vertSplitRect;      // pos/size of the splitter rect, 0 if not shown
    wxRect m_horizSplitRect;     // pos/size of the splitter rect, 0 if not shown

    void OnPaint( wxPaintEvent& event );
    void OnEraseBackground( wxEraseEvent& );
    void OnKeyDown( wxKeyEvent& event );
    void OnKeyUp( wxKeyEvent& event );
    void OnChar( wxKeyEvent& event );   // let wxWidgets translate numpad keys

    // ------ functions to get/send data (see also public functions)
    bool GetModelValues();
    bool SetModelValues();

private:
    void Init();
    friend class wxSheetTable;
    DECLARE_DYNAMIC_CLASS(wxSheet)
    DECLARE_EVENT_TABLE()
    DECLARE_NO_COPY_CLASS(wxSheet)
};

// ----------------------------------------------------------------------------
// wxSheetEvent
// ----------------------------------------------------------------------------
class WXDLLIMPEXP_SHEET wxSheetEvent : public wxNotifyEvent
{
public:
    wxSheetEvent(wxWindowID id = 0, wxEventType type = wxEVT_NULL,
                 wxObject* obj = NULL,
                 const wxSheetCoords &coords = wxNullSheetCoords,
                 const wxPoint &pos = wxPoint(-1, -1), bool sel = true);

    wxSheetEvent(const wxSheetEvent& event) : wxNotifyEvent(event),
                     m_coords(event.m_coords),
                     m_pos(event.m_pos), m_scrPos(event.m_scrPos),
                     m_selecting(event.m_selecting),
                     m_control(event.m_control), m_shift(event.m_shift),
                     m_alt(event.m_alt), m_meta(event.m_meta),
                     m_evtWin(event.m_evtWin) { }

    // Get the coords for the cell the event is for or the cursor cell.
    int   GetRow() const { return m_coords.m_row; }
    int   GetCol() const { return m_coords.m_col; }
    const wxSheetCoords& GetCoords() const { return m_coords; }

    // For (de)selection events
    bool  Selecting()   const { return m_selecting; }
    // Are these keys pressed, for events generated from key/mouse events
    bool  ControlDown() const { return m_control; }
    bool  ShiftDown()   const { return m_shift; }
    bool  AltDown()     const { return m_alt; }
    bool  MetaDown()    const { return m_meta; }

    // Get the unscrolled position of the mouse for a mouse generated event
    const wxPoint& GetPosition() const { return m_pos; }
    // Get the scrolled position of the mouse relative to the upper left of
    //   the window that it occured in.
    const wxPoint& GetScrolledPosition() const { return m_scrPos; }
    // Get the window that the event originally occured in.
    //   (for mouse and key events)
    //   example for wxEVT_SHEET_CELL_RIGHT_UP
    //   if (evt.GetEventWindow())
    //       evt.GetEventWindow()->PopupMenu(menu, evt.GetScrolledPosition());
    wxWindow* GetEventWindow() const { return m_evtWin; }

    // implementation

    // Setup the Ctrl/Shift/Alt/Meta keysDown and the mouse position from a
    //  wxKeyEvent or wxMouseEvent.
    //  The GetEventObject of this must be of type wxSheet
    bool SetKeysDownMousePos(wxEvent *mouseOrKeyEvent);

    virtual wxEvent *Clone() const { return new wxSheetEvent(*this); }

    wxSheetCoords m_coords;
    wxPoint       m_pos;
    wxPoint       m_scrPos;
    bool m_selecting;
    bool m_control;
    bool m_shift;
    bool m_alt;
    bool m_meta;
    wxWindow *m_evtWin;

    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxSheetEvent)
};

// ----------------------------------------------------------------------------
// wxSheetCellSizeEvent - wxEVT_SHEET_ROW/COL_SIZE/ING/ED
// ----------------------------------------------------------------------------
class WXDLLIMPEXP_SHEET wxSheetCellSizeEvent : public wxSheetEvent
{
public:
    wxSheetCellSizeEvent( wxWindowID id = 0, wxEventType type = wxEVT_NULL,
                          wxObject* obj = NULL,
                          const wxSheetCoords &coords = wxNullSheetCoords,
                          int size = 0 );

    wxSheetCellSizeEvent(const wxSheetCellSizeEvent& event)
        : wxSheetEvent(event) { }

    int GetSize() const { return GetInt(); }

    // implementation
    virtual wxEvent *Clone() const { return new wxSheetCellSizeEvent(*this); }

    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxSheetCellSizeEvent)
};

// ----------------------------------------------------------------------------
// wxSheetRangeSelectEvent - wxEVT_SHEET_RANGE_SELECTING(ED)
// ----------------------------------------------------------------------------
class WXDLLIMPEXP_SHEET wxSheetRangeSelectEvent : public wxSheetEvent
{
public:
    wxSheetRangeSelectEvent( wxWindowID id = 0, wxEventType type = wxEVT_NULL,
                             wxObject* obj = NULL,
                             const wxSheetBlock& block = wxNullSheetBlock,
                             bool sel = false, bool add_to_sel = false );

    wxSheetRangeSelectEvent(const wxSheetRangeSelectEvent& event)
        : wxSheetEvent(event), m_block(event.m_block), m_add(event.m_add) { }

    const wxSheetBlock& GetBlock() const { return m_block; }
    bool GetAddToSelection() const       { return m_add; }

    void SetBlock( const wxSheetBlock& block ) { m_block = block; }

    // wxPoint GetPosition() is unused
    // int GetCoords/Row/Col() is unused

    // implementation
    virtual wxEvent *Clone() const { return new wxSheetRangeSelectEvent(*this); }

    wxSheetBlock m_block;
    bool m_add;

    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxSheetRangeSelectEvent)
};

// ----------------------------------------------------------------------------
// wxSheetEditorCreatedEvent - wxEVT_SHEET_EDITOR_CREATED
// ----------------------------------------------------------------------------
class WXDLLIMPEXP_SHEET wxSheetEditorCreatedEvent : public wxCommandEvent
{
public:
    wxSheetEditorCreatedEvent( wxWindowID id = 0, wxEventType type = wxEVT_NULL,
                               wxObject* obj = NULL,
                               const wxSheetCoords& coords = wxNullSheetCoords,
                               wxWindow* ctrl = NULL );

    wxSheetEditorCreatedEvent(const wxSheetEditorCreatedEvent& evt)
        : wxCommandEvent(evt), m_coords(evt.m_coords), m_ctrl(evt.m_ctrl) { }


    const wxSheetCoords& GetCoords() const { return m_coords; }
    wxWindow* GetControl() const           { return m_ctrl; }

    void SetCoords(const wxSheetCoords& coords) { m_coords = coords; }
    void SetControl(wxWindow* ctrl)             { m_ctrl = ctrl; }

    // implementation
    virtual wxEvent *Clone() const { return new wxSheetEditorCreatedEvent(*this); }

    wxSheetCoords m_coords;
    wxWindow*     m_ctrl;

    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxSheetEditorCreatedEvent)
};

// ----------------------------------------------------------------------------
// events
// ----------------------------------------------------------------------------

BEGIN_DECLARE_EVENT_TYPES()
    // The origin of the grid window has changed
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_VIEW_CHANGED, 1592)

    // The grid cursor is about to be in a new cell, veto or !Skip() to block
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_SELECTING_CELL, 1592)
    // The grid cursor is in a new cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_SELECTED_CELL, 1592)

    // left down click in a grid cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_LEFT_DOWN, 1580)
    // right down click in a grid cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_RIGHT_DOWN, 1581)
    // left up click in a grid cell, sent after default processing
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_LEFT_UP, 1580)
    // right up click in a grid cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_RIGHT_UP, 1581)
    // left double click in a grid cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_LEFT_DCLICK, 1582)
    // right double click in a grid cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_RIGHT_DCLICK, 1583)

    // left down click in a label cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_LABEL_LEFT_DOWN, 1584)
    // right down click in a label cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_LABEL_RIGHT_DOWN, 1585)
    // left up click in a label cell, sent after default processing
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_LABEL_LEFT_UP, 1584)
    // right up click in a label cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_LABEL_RIGHT_UP, 1585)
    // left double click in a label cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_LABEL_LEFT_DCLICK, 1586)
    // right double click in a label cell
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_LABEL_RIGHT_DCLICK, 1587)

    // A row is about to be resized, the mouse is over an edge of cell
    // This is a wxSheetCellSizeEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_ROW_SIZE, 1588)
    // A row is being resized
    // This is a wxSheetCellSizeEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_ROW_SIZING, 1588)
    // A row has been resized, sent after default processing
    // This is a wxSheetCellSizeEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_ROW_SIZED, 1588)
    // A col is about to be resized, the mouse is over an edge of cell
    // This is a wxSheetCellSizeEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_COL_SIZE, 1589)
    // A col is being resized
    // This is a wxSheetCellSizeEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_COL_SIZING, 1589)
    // A col has been resized, sent after default processing
    // This is a wxSheetCellSizeEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_COL_SIZED, 1589)

    // A block of cells is about to be (de)selected (veto to stop)
    // This is a wxSheetRangeSelectEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_RANGE_SELECTING, 1590)
    // A block of cells has been (de)selected
    // This is a wxSheetRangeSelectEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_RANGE_SELECTED, 1590)

    // The value of a cell is about to be changed (veto to stop)
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_VALUE_CHANGING, 1591)
    // The value of a cell has been changed (veto to put old val back)
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_CELL_VALUE_CHANGED, 1591)

    // From EnableCellEditControl, the control is about to enabled (can veto)
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_EDITOR_ENABLED, 1593)
    // From DisableCellEditControl, the control is about to disabled (can veto)
    // This is a wxSheetEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_EDITOR_DISABLED, 1594)
    // From EnableCellEditControl, the edit control has been created
    // This is a wxSheetEditorCreatedEvent
    DECLARE_EXPORTED_EVENT_TYPE(WXDLLIMPEXP_SHEET, wxEVT_SHEET_EDITOR_CREATED, 1595)
END_DECLARE_EVENT_TYPES()

typedef void (wxEvtHandler::*wxSheetEventFunction)(wxSheetEvent&);
typedef void (wxEvtHandler::*wxSheetCellSizeEventFunction)(wxSheetCellSizeEvent&);
typedef void (wxEvtHandler::*wxSheetRangeSelectEventFunction)(wxSheetRangeSelectEvent&);
typedef void (wxEvtHandler::*wxSheetEditorCreatedEventFunction)(wxSheetEditorCreatedEvent&);

#define wxSheetEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxSheetEventFunction, &func)
#define wxSheetCellSizeEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxSheetCellSizeEventFunction, &func)
#define wxSheetRangeSelectEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxSheetRangeSelectEventFunction, &func)
#define wxSheetEditorCreatedEventHandler(func) \
    (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxSheetEditorCreatedEventFunction, &func)

#define wx__DECLARE_SHEETEVT(evt, id, fn)         wx__DECLARE_EVT1( evt, id, wxSheetEventHandler(fn))
#define wx__DECLARE_SHEETCELLSIZEEVT(evt, id, fn) wx__DECLARE_EVT1( evt, id, wxSheetCellSizeEventHandler(fn))
#define wx__DECLARE_SHEETRANGESELEVT(evt, id, fn) wx__DECLARE_EVT1( evt, id, wxSheetRangeSelectEventHandler(fn))
#define wx__DECLARE_SHEETEDITOREVT(evt, id, fn)   wx__DECLARE_EVT1( evt, id, wxSheetEditorCreatedEventHandler(fn))

#define EVT_SHEET_VIEW_CHANGED(id, fn)        wx__DECLARE_SHEETEVT( wxEVT_SHEET_VIEW_CHANGED,        id, fn )
#define EVT_SHEET_SELECTING_CELL(id, fn)      wx__DECLARE_SHEETEVT( wxEVT_SHEET_SELECTING_CELL,      id, fn )
#define EVT_SHEET_SELECTED_CELL(id, fn)       wx__DECLARE_SHEETEVT( wxEVT_SHEET_SELECTED_CELL,       id, fn )
#define EVT_SHEET_CELL_LEFT_DOWN(id, fn)      wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_LEFT_DOWN,      id, fn )
#define EVT_SHEET_CELL_RIGHT_DOWN(id, fn)     wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_RIGHT_DOWN,     id, fn )
#define EVT_SHEET_CELL_LEFT_UP(id, fn)        wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_LEFT_UP,        id, fn )
#define EVT_SHEET_CELL_RIGHT_UP(id, fn)       wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_RIGHT_UP,       id, fn )
#define EVT_SHEET_CELL_LEFT_DCLICK(id, fn)    wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_LEFT_DCLICK,    id, fn )
#define EVT_SHEET_CELL_RIGHT_DCLICK(id, fn)   wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_RIGHT_DCLICK,   id, fn )
#define EVT_SHEET_LABEL_LEFT_DOWN(id, fn)     wx__DECLARE_SHEETEVT( wxEVT_SHEET_LABEL_LEFT_DOWN,     id, fn )
#define EVT_SHEET_LABEL_RIGHT_DOWN(id, fn)    wx__DECLARE_SHEETEVT( wxEVT_SHEET_LABEL_RIGHT_DOWN,    id, fn )
#define EVT_SHEET_LABEL_LEFT_UP(id, fn)       wx__DECLARE_SHEETEVT( wxEVT_SHEET_LABEL_LEFT_UP,       id, fn )
#define EVT_SHEET_LABEL_RIGHT_UP(id, fn)      wx__DECLARE_SHEETEVT( wxEVT_SHEET_LABEL_RIGHT_UP,      id, fn )
#define EVT_SHEET_LABEL_LEFT_DCLICK(id, fn)   wx__DECLARE_SHEETEVT( wxEVT_SHEET_LABEL_LEFT_DCLICK,   id, fn )
#define EVT_SHEET_LABEL_RIGHT_DCLICK(id, fn)  wx__DECLARE_SHEETEVT( wxEVT_SHEET_LABEL_RIGHT_DCLICK,  id, fn )
#define EVT_SHEET_ROW_SIZE(id, fn)            wx__DECLARE_SHEETCELLSIZEEVT( wxEVT_SHEET_ROW_SIZE,    id, fn )
#define EVT_SHEET_ROW_SIZING(id, fn)          wx__DECLARE_SHEETCELLSIZEEVT( wxEVT_SHEET_ROW_SIZING,  id, fn )
#define EVT_SHEET_ROW_SIZED(id, fn)           wx__DECLARE_SHEETCELLSIZEEVT( wxEVT_SHEET_ROW_SIZED,   id, fn )
#define EVT_SHEET_COL_SIZE(id, fn)            wx__DECLARE_SHEETCELLSIZEEVT( wxEVT_SHEET_COL_SIZE,    id, fn )
#define EVT_SHEET_COL_SIZING(id, fn)          wx__DECLARE_SHEETCELLSIZEEVT( wxEVT_SHEET_COL_SIZING,  id, fn )
#define EVT_SHEET_COL_SIZED(id, fn)           wx__DECLARE_SHEETCELLSIZEEVT( wxEVT_SHEET_COL_SIZED,   id, fn )
#define EVT_SHEET_RANGE_SELECTING(id, fn)     wx__DECLARE_SHEETRANGESELEVT( wxEVT_SHEET_RANGE_SELECTING, id, fn )
#define EVT_SHEET_RANGE_SELECTED(id, fn)      wx__DECLARE_SHEETRANGESELEVT( wxEVT_SHEET_RANGE_SELECTED,  id, fn )
#define EVT_SHEET_CELL_VALUE_CHANGING(id, fn) wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_VALUE_CHANGING,  id, fn )
#define EVT_SHEET_CELL_VALUE_CHANGED(id, fn)  wx__DECLARE_SHEETEVT( wxEVT_SHEET_CELL_VALUE_CHANGED,   id, fn )
#define EVT_SHEET_EDITOR_ENABLED(id, fn)      wx__DECLARE_SHEETEVT( wxEVT_SHEET_EDITOR_ENABLED,       id, fn )
#define EVT_SHEET_EDITOR_DISABLED(id, fn)     wx__DECLARE_SHEETEVT( wxEVT_SHEET_EDITOR_DISABLED,      id, fn )
#define EVT_SHEET_EDITOR_CREATED(id, fn)      wx__DECLARE_SHEETEDITOREVT( wxEVT_SHEET_EDITOR_CREATED, id, fn )

#if 0  // TODO: implement these ?  others ?

wxEVT_SHEET_CREATE_CELL;
wxEVT_SHEET_CHANGE_LABELS;
wxEVT_SHEET_CHANGE_SEL_LABEL;

#define EVT_SHEET_CREATE_CELL(id, fn)      DECLARE_EVENT_TABLE_ENTRY( wxEVT_SHEET_CREATE_CELL,      id, wxID_ANY, (wxObjectEventFunction) (wxEventFunction) (wxSheetEventFunction) &fn, NULL ),
#define EVT_SHEET_CHANGE_LABELS(id, fn)    DECLARE_EVENT_TABLE_ENTRY( wxEVT_SHEET_CHANGE_LABELS,    id, wxID_ANY, (wxObjectEventFunction) (wxEventFunction) (wxSheetEventFunction) &fn, NULL ),
#define EVT_SHEET_CHANGE_SEL_LABEL(id, fn) DECLARE_EVENT_TABLE_ENTRY( wxEVT_SHEET_CHANGE_SEL_LABEL, id, wxID_ANY, (wxObjectEventFunction) (wxEventFunction) (wxSheetEventFunction) &fn, NULL ),

#endif // 0

#endif  // __WX_SHEET_H__
